/*
 * Ext Gantt 1.2
 * Copyright(c) 2009-2011 Mats Bryntse Consulting
 * mats@ext-scheduler.com
 * http://www.ext-scheduler.com/license.html
 *
 */
 
Ext.ns('Sch.gantt');

/**
 * @class Sch.gantt.DependencyPainter
 * @extends Ext.util.Observable
 * <p>Internal class handling the drawing of inter-task dependencies.</p>
 */
Sch.gantt.DependencyPainter = Ext.extend(Ext.util.Observable, {
    
    rowHeight : 22,

    constructor : function (g, cfg) {    
        var view = g.getView();

        cfg = cfg || {};
       
        Ext.apply(this, cfg, {
            buffered : view.isBuffered,
            grid : g,
            taskStore : g.getTaskStore(),
            view : view,
            containerEl : view.scroller,
            xOffset : 8,
            yOffset : this.rowHeight/2,
            midRowOffset : 4,
            arrowOffset : 8
        });
        
        if (!this.lineTpl) {
            var line = '<div class="sch-dependency sch-dep-{parent.id} sch-dependency-line" style="left:{[Math.min(values.x1, values.x2)]}px;top:{[Math.min(values.y1, values.y2)]}px;width:{[Math.abs(values.x1-values.x2)]}px;height:{[Math.abs(values.y1-values.y2)]}px"></div>',
                ieLine = '<div class="sch-dependency sch-dep-{parent.id} sch-dependency-line" style="left:{[Math.min(values.x1, values.x2)]}px;top:{[Math.min(values.y1, values.y2)]}px;width:{[Math.abs(values.x1-values.x2)+2]}px;height:{[Math.abs(values.y1-values.y2)+2]}px"></div>';
            
            var arrow = '<img src="' + Ext.BLANK_IMAGE_URL + '" class="sch-dependency sch-dep-{id} sch-dependency-arrow sch-dependency-arrow-{[this.getArrowDirection(values.lineCoordinates)]}" style="left:{[values.lineCoordinates[values.lineCoordinates.length - 1].x2]}px;top:{[values.lineCoordinates[values.lineCoordinates.length - 1].y2]}px"/>';

            var ieArrow = '<div style="left:{[values.lineCoordinates[values.lineCoordinates.length - 1].x2]}px;top:{[values.lineCoordinates[values.lineCoordinates.length - 1].y2]}px" class="sch-dependency-arrow-ct sch-dependency sch-dep-{id} "><img src="' + Ext.BLANK_IMAGE_URL + '" class="sch-dependency-arrow sch-dependency-arrow-{[this.getArrowDirection(values.lineCoordinates)]}" /></div>';

            this.lineTpl = new Ext.XTemplate(
                '<tpl for=".">'+
                    '<tpl for="lineCoordinates">'+
                        (Ext.isBorderBox ? ieLine : line)+
                    '</tpl>' +
                    (Ext.isIE ? ieArrow : arrow) +
                '</tpl>',
                {
                    getArrowDirection : function(coords) {
                        var lastXY = coords[coords.length-1];
                        if (lastXY.x1 === lastXY.x2) {
                            return 'down';
                        } else if (lastXY.x1 > lastXY.x2) {
                            return 'left';
                        } else {
                            return 'right';
                        }
                    }
                }
            ); 
        }
        this.lineTpl.compile();
    },

    getTaskBox : function(task) {
        var ts = this.taskStore;
        
        // Check if element is inside a collapsed parent task
        if (!task || !ts.isVisibleNode(task)) {
            return null;
        }

        var v = this.view,
            left = v.getXFromDate(task.get('StartDate'));
            
        if (left < 0) {
            return null;
        }

        var right = v.getXFromDate(task.get('EndDate')),
            taskIndex = ts.indexOf(task); // Store could be filtered

        if (right < 0 || taskIndex < 0) {
            return null;
        }

        var g = this.grid,
            el = Ext.get(v.getRow(taskIndex));

        if (!el) {
            return null;
        }

        var firstChild = el.first();
        
        el = firstChild || el;
            
        var xOffset = g.getXOffset(task),
            offsets = el.getOffsetsTo(v.scroller),
            top = offsets[1] + v.scroller.getScroll().top;
        
        left -= xOffset;
        right += xOffset - 1;

        return new Ext.lib.Region(top, right, top + el.getHeight(), left);
    },
    
    draw : function(dependencyRecord) {
        var me = this,
            ts = me.taskStore;
        
        // Normalize input
        if (!Ext.isArray(dependencyRecord)) {
            dependencyRecord = [dependencyRecord];
        }

        if (dependencyRecord.length === 0 || ts.getCount() <= 0) {
            return;
        }
        
        var depData = [],
            buffered = me.buffered,
            view = me.view,
            firstVis, lastVis, coords, fromTask, toTask, fromBox, toBox, r;

        if (buffered) {
            var visibleRows = view.getVisibleRows();
            firstVis = visibleRows.first;
            lastVis = visibleRows.last;
        }

        for (var i = 0, l = dependencyRecord.length; i < l; i++) {
            r = dependencyRecord[i];
            fromTask = ts.getById(r.get('From'));
            toTask = ts.getById(r.get('To'));
            
            if (!buffered || me.intersectsViewport(fromTask, toTask, firstVis, lastVis)) {
                
                fromBox = me.getTaskBox(fromTask);
                toBox = me.getTaskBox(toTask);
                
                if (fromBox && toBox) {
           
                    switch(r.get('Type')) {
                        case Sch.gantt.Dependency.StartToEnd:
                            coords = me.getStartToEndCoordinates(fromBox, toBox);
                        break;
            
                        case Sch.gantt.Dependency.StartToStart:
                            coords = me.getStartToStartCoordinates(fromBox, toBox);
                        break;
            
                        case Sch.gantt.Dependency.EndToStart:
                            coords = me.getEndToStartCoordinates(fromBox, toBox);
                        break;
            
                        case Sch.gantt.Dependency.EndToEnd:
                            coords = me.getEndToEndCoordinates(fromBox, toBox);
                        break;
            
                        default:
                            throw 'Invalid case statement';
                        break;
                    }
                    depData.push({
                        lineCoordinates : coords,
                        id : r.id
                    });
                }
            }
        }

        me.lineTpl.insertFirst(me.containerEl, depData);
    },

    intersectsViewport : function(task1, task2, firstVisibleRowIndex, lastVisibleRowIndex) {
        var index1 = this.taskStore.indexOf(task1),
            index2 = this.taskStore.indexOf(task2);

        return !((index1 < firstVisibleRowIndex && index2 < firstVisibleRowIndex) ||
                (index1 > lastVisibleRowIndex && index2 > lastVisibleRowIndex));
    },

    getStartToStartCoordinates : function(fromBox, toBox, firstVisibleRow, lastVisibleRow) {
        var x1 = fromBox.left, 
            y1 = fromBox.top + (fromBox.bottom - fromBox.top)/2, 
            x2 = toBox.left, 
            y2 = toBox.top + (toBox.bottom - toBox.top)/2, 
            y2offset = fromBox.top < toBox.top ? (y2 - this.yOffset - this.midRowOffset) : (y2 + this.yOffset + this.midRowOffset),
            leftPointOffset = this.xOffset + this.arrowOffset;
        
        if (x1 > (x2 + this.xOffset)) {
            leftPointOffset += (x1 - x2);
        }
        
        return [
            {
                x1 : x1, 
                y1 : y1, 
                x2 : x1 - leftPointOffset,
                y2 : y1
            },
            {
               x1 : x1 - leftPointOffset, 
               y1 : y1, 
               x2 : x1 - leftPointOffset, 
               y2 : y2
            },
            {
                x1 : x1 - leftPointOffset, 
                y1 : y2, 
                x2 : x2 - this.arrowOffset, 
                y2 : y2
            }
        ];
    },
    
    getStartToEndCoordinates : function(fromBox, toBox) {
        var x1 = fromBox.left, 
            y1 = fromBox.top + (fromBox.bottom - fromBox.top)/2, 
            x2 = toBox.right, 
            y2 = toBox.top + (toBox.bottom - toBox.top)/2, 
            y2offset = fromBox.top < toBox.top ? (y2 - this.yOffset - this.midRowOffset) : (y2 + this.yOffset + this.midRowOffset),
            coords,
            leftOffset;
        
         if (x2 > (x1 + this.xOffset - this.arrowOffset) ||
             Math.abs(x2 - x1) < (2*(this.xOffset + this.arrowOffset)))
         { 
            leftOffset = x1 - this.xOffset - this.arrowOffset;
            var x2Offset = x2 + this.xOffset + this.arrowOffset;

            // To after from
            // --|
            // |-----------
            //             |
            //          <--|
            coords = [
                {
                    x1 : x1, 
                    y1 : y1, 
                    x2 : leftOffset,
                    y2 : y1
                },
                {
                   x1 : leftOffset, 
                   y1 : y1, 
                   x2 : leftOffset, 
                   y2 : y2offset
                },
                {
                    x1 : leftOffset, 
                    y1 : y2offset, 
                    x2 : x2Offset, 
                    y2 : y2offset
                },
                {
                    x1 : x2Offset, 
                    y1 : y2offset, 
                    x2 : x2Offset, 
                    y2 : y2
                },
                {
                    x1 : x2Offset, 
                    y1 : y2, 
                    x2 : x2 + this.arrowOffset, 
                    y2 : y2
                }
            ];
         }
         else {
            // From after to
            //    -----|
            // <--|
            //     
            leftOffset = x1 - this.xOffset - this.arrowOffset;
            
            coords = [
                {
                    x1 : x1, 
                    y1 : y1, 
                    x2 : leftOffset,
                    y2 : y1
                },
                {
                   x1 : leftOffset, 
                   y1 : y1, 
                   x2 : leftOffset, 
                   y2 : y2
                },
                {
                    x1 : leftOffset, 
                    y1 : y2, 
                    x2 : x2 + this.arrowOffset, 
                    y2 : y2
                }
            ];
        }
        return coords;
    },
    
    getEndToStartCoordinates : function(fromBox, toBox) {
       
        var x1 = fromBox.right, 
            y1 = fromBox.top + (fromBox.bottom - fromBox.top)/2, 
            x2 = toBox.left, 
            y2 = toBox.top + (toBox.bottom - toBox.top)/2, 
            y2offset = fromBox.top < toBox.top ? (y2 - this.yOffset - this.midRowOffset) : (y2 + this.yOffset + this.midRowOffset),
            coords,
            leftOffset;
         
         if (x2 >= (x1 - 6) && y2 > y1) { 
            /* To after from
             * ---
             *   |
             */
            
            leftOffset = Math.max(x1 - 6, x2) + this.xOffset;
            y2 = toBox.top;
           
            coords = [
                {
                    x1 : x1, 
                    y1 : y1, 
                    x2 : leftOffset,
                    y2 : y1
                },
                {
                    x1 : leftOffset, 
                    y1 : y1, 
                    x2 : leftOffset, 
                    y2 : y2 - this.arrowOffset
                }
            ];
         }
         else {
            /* From after to
             *        -
             *        |
             *     ----
             *    |-> 
            */     
            leftOffset = x1 + this.xOffset + this.arrowOffset;
            var x2Offset = x2 - this.xOffset - this.arrowOffset;
            coords = [
                {
                    x1 : x1, 
                    y1 : y1, 
                    x2 : leftOffset,
                    y2 : y1
                },
                {
                   x1 : leftOffset, 
                   y1 : y1, 
                   x2 : leftOffset, 
                   y2 : y2offset
                },
                {
                    x1 : leftOffset, 
                    y1 : y2offset, 
                    x2 : x2Offset, 
                    y2 : y2offset
                },
                {
                    x1 : x2Offset, 
                    y1 : y2offset, 
                    x2 : x2Offset, 
                    y2 : y2
                },
                {
                    x1 : x2Offset, 
                    y1 : y2, 
                    x2 : x2 - this.arrowOffset, 
                    y2 : y2
                }
            ];
        }
        return coords;
    },
   
    getEndToEndCoordinates : function(fromBox, toBox) {
       
        var x1 = fromBox.right, 
            y1 = fromBox.top + (fromBox.bottom - fromBox.top)/2, 
            x2 = toBox.right + this.arrowOffset, 
            y2 = toBox.top + (toBox.bottom - toBox.top)/2, 
            rightPointOffset = x2 + this.xOffset + this.arrowOffset;
        
        if (x1 > (x2 + this.xOffset)) {
            rightPointOffset += x1 - x2;
        }
         
        return [
            {
                x1 : x1, 
                y1 : y1, 
                x2 : rightPointOffset,
                y2 : y1
            },
            {
               x1 : rightPointOffset, 
               y1 : y1, 
               x2 : rightPointOffset, 
               y2 : y2
            },
            {
                x1 : rightPointOffset, 
                y1 : y2, 
                x2 : x2, 
                y2 : y2
            }
        ];
    }
});
